/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "i2c_if.h"
#include "iot_main.h"

#define BH1750_ADDR 0x23 /* slave address */
#define SHT30_ADDR  0x44 /* slave address */

static uint8_t bh1750_init_cmd[] = {0x01};
static uint8_t bh1750_start_cmd[] = {0x10};

static uint8_t sht30_init_cmd[] = {0x22, 0x36};
static uint8_t sht30_read_cmd[] = {0xE0, 0x00};

static float g_humidity = 0.0;

/**
 * default I2C0_IOMUX_INDEX 4, SCL - PIN04, SDA - PIN05
 */
struct e53_ia1 {
    DevHandle i2c;
    uint16_t i2c_id;
    uint16_t i2c_bh1750_addr;
    uint16_t i2c_sht30_addr;
};

static struct e53_ia1 priv = {
    .i2c_id = 1,
    .i2c_bh1750_addr = BH1750_ADDR,
    .i2c_sht30_addr = SHT30_ADDR,
};

static void bh1750_init(struct e53_ia1 *priv)
{
    struct I2cMsg msg = {
        .addr = priv->i2c_bh1750_addr,
        .flags = 0,
        .len = sizeof(bh1750_init_cmd),
        .buf = bh1750_init_cmd,
    };
    if (I2cTransfer(priv->i2c, &msg, 1) != 1) {
        LOG_E("bh1750_init failed");
        return;
    }
    osDelay(40);
}

static void bh1750_start(struct e53_ia1 *priv)
{
    struct I2cMsg msg = {
        .addr = priv->i2c_bh1750_addr,
        .flags = 0,
        .len = sizeof(bh1750_start_cmd),
        .buf = bh1750_start_cmd,
    };
    
    if (I2cTransfer(priv->i2c, &msg, 1) != 1) {
        LOG_E("bh1750_start failed");
        return;
    }
}

static void sht30_init(struct e53_ia1 *priv)
{
    struct I2cMsg msg = {
        .addr = priv->i2c_sht30_addr,
        .flags = 0,
        .len = sizeof(sht30_init_cmd),
        .buf = sht30_init_cmd,
    };
    if (I2cTransfer(priv->i2c, &msg, 1) != 1) {
        LOG_E("sht30_init failed");
        return;
    }
    osDelay(40);
}

/***************************************************************
* 函数名称: SHT3x_CheckCrc
* 说    明: 检查数据正确性
* 参    数: data：读取到的数据
						nbrOfBytes：需要校验的数量
						checksum：读取到的校对比验值
* 返 回 值: 校验结果，0-成功		1-失败
***************************************************************/
static uint8_t SHT3x_CheckCrc(uint8_t data[], uint8_t nbrOfBytes, uint8_t checksum)
{

    uint8_t crc = 0xFF;
    uint8_t bit = 0;
    uint8_t byteCtr;
    const int16_t POLYNOMIAL = 0x131;
    //calculates 8-Bit checksum with given polynomial
    for (byteCtr = 0; byteCtr < nbrOfBytes; ++byteCtr)
    {
        crc ^= (data[byteCtr]);
        for (bit = 8; bit > 0; --bit)
        {
            if (crc & 0x80)
                crc = (crc << 1) ^ POLYNOMIAL;
            else
                crc = (crc << 1);
        }
    }

    if (crc != checksum)
        return 1;
    else
        return 0;
}

/***************************************************************
* 函数名称: SHT3x_CalcTemperatureC
* 说    明: 温度计算
* 参    数: u16sT：读取到的温度原始数据
* 返 回 值: 计算后的温度数据
***************************************************************/
static float SHT3x_CalcTemperatureC(uint16_t u16sT)
{

    float temperatureC = 0;

    u16sT &= ~0x0003;

    temperatureC = (175 * (float)u16sT / 65535 - 45);

    return temperatureC;
}

/***************************************************************
* 函数名称: SHT3x_CalcRH
* 说    明: 湿度计算
* 参    数: u16sRH：读取到的湿度原始数据
* 返 回 值: 计算后的湿度数据
***************************************************************/
static float SHT3x_CalcRH(uint16_t u16sRH)
{

    float humidityRH = 0;

    u16sRH &= ~0x0003;

    humidityRH = (100 * (float)u16sRH / 65535);

    return humidityRH;
}

static void e53_ia1_read_temperature_humidity_lux(struct e53_ia1 *priv, float *temperature, float *humidity, float *lux)
{
    uint8_t lux_data[2] = {0};
    uint8_t temperature_humidity_data[6] = {0};
    struct I2cMsg msgs[] = {
        {
            .addr = priv->i2c_bh1750_addr,
            .flags = I2C_FLAG_READ,
            .len = sizeof(lux_data),
            .buf = lux_data,
        },
        {
            .addr = priv->i2c_sht30_addr,
            .flags = 0,
            .len = sizeof(sht30_read_cmd),
            .buf = sht30_read_cmd,
        },
        {
            .addr = priv->i2c_sht30_addr,
            .flags = I2C_FLAG_READ,
            .len = sizeof(temperature_humidity_data),
            .buf = temperature_humidity_data,
        },
    };

    bh1750_start(priv);
    osDelay(180);
    if (I2cTransfer(priv->i2c, &msgs[0], 1) != 1) {
        LOG_E("lux detect failed");
        return;
    }
    *lux = (float)(((lux_data[0] << 8) + lux_data[1]) / 1.2);

    uint8_t data[3];
    uint16_t dat, tmp;

    if (I2cTransfer(priv->i2c, &msgs[1], 1) != 1) {
        LOG_E("temperature and humidity detect cmd failed");
        return;
    }
    if (I2cTransfer(priv->i2c, &msgs[2], 1) != 1) {
        LOG_E("temperature and humidity detect failed");
        return;
    }

    data[0] = temperature_humidity_data[0];
    data[1] = temperature_humidity_data[1];
    data[2] = temperature_humidity_data[2];

    tmp = SHT3x_CheckCrc(data, 2, data[2]);
    if (!tmp)
    {
        dat = ((uint16_t)data[0] << 8) | data[1];
        *temperature = SHT3x_CalcTemperatureC(dat);
    }

    data[0] = temperature_humidity_data[3];
    data[1] = temperature_humidity_data[4];
    data[2] = temperature_humidity_data[5];

    tmp = SHT3x_CheckCrc(data, 2, data[2]);
    if (!tmp)
    {
        dat = ((uint16_t)data[0] << 8) | data[1];
        *humidity = SHT3x_CalcRH(dat);
    }

}

void i2c_Init(void)
{
    priv.i2c = I2cOpen(priv.i2c_id);
    DBG_ASSERT(priv.i2c != NULL);
    bh1750_init(&priv);
    sht30_init(&priv);
}

float i2c_GetHumidity()
{
    float temperature = 0.0;
    float humidity = 0.0;
    float lux = 0.0;
    
    e53_ia1_read_temperature_humidity_lux(&priv, &temperature, &humidity, &lux);
    return humidity;
    
}